package com.markerhub.common.shiro;

import com.markerhub.common.fileter.CORSAuthenticationFilter;
import com.markerhub.common.shiro.cache.ShiroRedisCacheManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;


/**
 * 类名称:shiro配置类
 */
@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean(步骤3)
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        //==== 1 ===  设置拦截器的map
        //设置shiro中的拦截器
        HashMap<String, Filter> filters = new HashMap<>();
        //修改logout拦截器
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl("/user/logoutsuccess");
        filters.put("logout",logoutFilter);
        //处理跨域问题的拦截器
        filters.put("corsAuthenticationFilter", corsAuthenticationFilter());
        //将拦截器设置给shiro
        shiroFilterFactoryBean.setFilters(filters);

        // 前后端分离 未登录 跳转到 控制器来返回JSON
        shiroFilterFactoryBean.setLoginUrl("/user/unauth");

        //==== 2 ===  设置拦截url的map
        Map<String,String> map =  new LinkedHashMap<>();
        //authc:所有url必须通过认证才能访问，anon:所有url都可以匿名访问
        //设置不会被拦截的url，顺序判断
        //1、关于swagger的不需要拦截
        map.put("/swagger","anon");
        map.put("/swagger-ui.html", "anon");
        map.put("/swagger-resources", "anon");
        map.put("/swagger-resources/configuration/security", "anon");
        map.put("/swagger-resources/configuration/ui", "anon");
        map.put("/v2/api-docs", "anon");
        map.put("/webjars/**", "anon");
        //2、其他不需要拦截的
        map.put("/user/login","anon");
        map.put("/user/captcha","anon");
        map.put("/user/unauth","anon");
        map.put("/user/logoutsuccess","anon");
        //设置会被拦截的url
        map.put("/user/logout","logout");//shiro自带的logout过滤器，会清除session和缓存
 //       map.put("/**","corsAuthenticationFilter");//用我们自定义的拦截器拦截所有请求
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(MyRealm myRealm,SessionManager sessionManager,RedisTemplate<Object, Object> template){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();

        //设置ehcache缓存
        securityManager.setCacheManager(shiroRedisCacheManager(template));
        //设置自定义realm
        securityManager.setRealm(myRealm);
        //设置自定义sessionManager
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }
    //创建realm对象(步骤1)

    @Bean
    public MyRealm myRealm(){
        MyRealm myRealm = new MyRealm();
        //设置hashed凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //设置md5加密
        credentialsMatcher.setHashAlgorithmName("md5");
        //绑定hashed凭证匹配器
        myRealm.setCredentialsMatcher(credentialsMatcher);

        //开启缓存管理器,减小授权查表给数据库带来的压力
        myRealm.setCachingEnabled(true);
        // 因为未实现session缓存，存在认证缓存的时候不会走认证方法，导致session中没有用户的数据，后续ShiroUtils会出现空指针异常，暂时关闭缓存
//        myRealm.setAuthenticationCachingEnabled(false);

        myRealm.setAuthenticationCacheName("authenticationCache");
//        myRealm.setAuthorizationCachingEnabled(false);//暂时关闭缓存
        myRealm.setAuthorizationCachingEnabled(true);
        myRealm.setAuthorizationCacheName("authorizationCache");

        return myRealm;
    }

    public ShiroRedisCacheManager shiroRedisCacheManager(RedisTemplate template){
        ShiroRedisCacheManager shiroRedisCacheManager = new ShiroRedisCacheManager(template);
        return shiroRedisCacheManager;
    }

    @Bean
    public SessionManager sessionManager() {
        MySessionManager sessionManager = new MySessionManager();
        // 设置session过期时间3600s
        sessionManager.setGlobalSessionTimeout(3600000L);
        System.out.println("insert sessionManager()");
        return sessionManager;
    }

    public CORSAuthenticationFilter corsAuthenticationFilter() {
        return new CORSAuthenticationFilter();
    }

    /**
     * 开启Shiro注解(如@RequiresRoles,@RequiresPermissions),
     * 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
